using System;
using Net.Helper;
using Net.Share;
using Net.System;
using Cysharp.Threading.Tasks;
using System.Collections.Generic;

namespace Net.Client
{
    /// <summary>
    /// 网络客户端核心基类 2025.3.27
    /// </summary>
    public abstract partial class ClientBase : Client, INetClient, ISendHandle
    {
        /// <summary>
        /// 待发送的操作列表
        /// </summary>
        private readonly ListSafe<Operation> operations = new();

        /// <summary>
        /// 当使用服务器的NetScene.AddOperation方法时调用， 场景内的所有演员行为同步
        /// </summary>
        public OnOperationSyncEvent OnOperationSync { get; set; }

        /// <summary>
        /// 当属性同步-- 当MysqlBuild生成的类属性在服务器被修改后同步下来会调用此事件
        /// </summary>
        public OnReceiveDataHandle OnPropertySync { get; set; }

        /// <summary>
        /// 网络客户端实例
        /// </summary>
        public static ClientBase Instance { get; set; }

        /// <summary>
        /// 是否使用unity主线程进行每一帧更新？  
        /// True：使用unity的Update等方法进行更新，unity的组建可以在Rpc函数内进行调用。
        /// False：使用多线程进行网络更新，使用多线程更新后unity的组件将不能在rpc函数内进行赋值设置等操作，否则会出现错误问题!
        /// </summary>
        public bool UseUnityThread { get; set; }

        public override void OnNetworkTick()
        {
            if (!UseUnityThread)
                base.OnNetworkTick();
            SyncVarHandler();
        }

        public override void Update()
        {
            if (!openClient) //如果被服务器强制断开, 则不需要触发重连了
                return;
            if (UpdateMode == NetworkUpdateMode.Custom)
                NetworkTick();
            if (UseUnityThread) //使用unity线程才需要这样更新, 如果使用客户端池就不需要
                RpcAdapter.Execute();
        }

        #region 操作同步

        /// <summary>
        /// 添加操作, 跟Send方法类似，区别在于AddOperation方法是将所有要发送的数据收集成一堆数据后，等待时间间隔进行发送。
        /// 而Send则是直接发送
        /// </summary>
        /// <param name="func"></param>
        /// <param name="pars"></param>
        [Obsolete("此方法尽量少用,此方法有可能产生较大的数据，不要频繁发送!", false)]
        public void AddOperation(string func, params object[] pars) => AddOperation(NetCmd.CallRpcHash, func, pars);

        /// <summary>
        /// 添加操作, 跟Send方法类似，区别在于AddOperation方法是将所有要发送的数据收集成一堆数据后，等待时间间隔进行发送。
        /// 而Send则是直接发送
        /// </summary>
        /// <param name="func"></param>
        /// <param name="pars"></param>
        [Obsolete("此方法尽量少用,此方法有可能产生较大的数据，不要频繁发送!", false)]
        public void AddOperation(uint func, params object[] pars) => AddOperation(NetCmd.CallRpcHash, func, pars);

        /// <summary>
        /// 添加操作, 跟Send方法类似，区别在于AddOperation方法是将所有要发送的数据收集成一堆数据后，等待时间间隔进行发送。
        /// 而Send则是直接发送
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="func"></param>
        /// <param name="pars"></param>
        [Obsolete("此方法尽量少用,此方法有可能产生较大的数据，不要频繁发送!", false)]
        public void AddOperation(byte cmd, string func, params object[] pars) => AddOperation(cmd, func.CRCU32(), pars);

        /// <summary>
        /// 添加操作, 跟Send方法类似，区别在于AddOperation方法是将所有要发送的数据收集成一堆数据后，等待时间间隔进行发送。
        /// 而Send则是直接发送
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="func"></param>
        /// <param name="pars"></param>
        [Obsolete("此方法尽量少用,此方法有可能产生较大的数据，不要频繁发送!", false)]
        public void AddOperation(byte cmd, uint func, params object[] pars)
        {
            var segment = BufferPool.Take();
            SerializeAdapter.OnSerializeRpc(segment, RpcUnitFactory.Create(cmd: cmd, kernel: true, protocol: func, pars: pars));
            var opt = new Operation(cmd, segment.ToArray(true));
            AddOperation(opt);
        }

        /// <summary>
        /// 添加操作, 跟Send方法类似，区别在于AddOperation方法是将所有要发送的数据收集成一堆数据后，等待时间间隔进行发送。
        /// 而Send则是直接发送
        /// </summary>
        /// <param name="opt"></param>
        public void AddOperation(in Operation opt) => operations.Add(opt);

        /// <summary>
        /// 添加操作, 跟Send方法类似，区别在于AddOperation方法是将所有要发送的数据收集成一堆数据后，等待时间间隔进行发送。
        /// 而Send则是直接发送
        /// </summary>
        /// <param name="opts"></param>
        public void AddOperations(IEnumerable<Operation> opts)
        {
            foreach (Operation opt in opts)
                AddOperation(opt);
        }

        public override void SendDirect()
        {
            SendOperations();
            base.SendDirect();
        }

        protected virtual void SendOperations()
        {
            int count = operations.Count;
            if (count <= 0)
                return;
            while (count > 500)
            {
                OnOptPacket(500);
                count -= 500;
            }

            if (count <= 0)
                return;
            OnOptPacket(count);
        }

        protected virtual void OnOptPacket(int count)
        {
            var operations1 = operations.GetRemoveRange(0, count);
            var operList = new OperationList(operations1);
            var buffer = SerializeAdapter.OnSerializeOpt(operList);
            RpcQueue.Enqueue(RpcUnitFactory.Create(cmd: NetCmd.OperationSyncHash, kernel: false, buffer: buffer, serialize: SyncSerialize.False));
        }

        #endregion

        protected override void OnCommand4Handler(IRpcModel model, ISegment segment)
        {
            switch (model.Cmd)
            {
                case NetCmd.CallRpcHash:
                    if (model.Kernel)
                        RpcAdapter.Enqueue(this, model);
                    else
                        MainThreadEnqueue(model);
                    break;
                case NetCmd.LocalHash:
                    if (model.Kernel)
                        RpcAdapter.Enqueue(this, model);
                    else
                        MainThreadEnqueue(model);
                    break;
                case NetCmd.SceneHash:
                    if (model.Kernel)
                        RpcAdapter.Enqueue(this, model);
                    else
                        MainThreadEnqueue(model);
                    break;
                case NetCmd.NoticeHash:
                    if (model.Kernel)
                        RpcAdapter.Enqueue(this, model);
                    else
                        MainThreadEnqueue(model);
                    break;
                case NetCmd.ThreadRpcHash:
                    if (model.Kernel)
                        RpcAdapter.Enqueue(this, model);
                    else
                        OnReceiveData?.Invoke(model); //这里是多线程调用,不切换到主线程了
                    break;
                case NetCmd.OperationSyncHash:
                    var operList = SerializeAdapter.OnDeserializeOpt(segment);
                    MainThreadEnqueue(OnOperationSync, operList);
                    break;
                case NetCmd.SyncVarP2PHash:
                    SyncVarHelper.SyncVarHandler(RpcAdapter.SyncVarDic, model.GetBuffer);
                    break;
                case NetCmd.SyncPropertyDataHash:
                    MainThreadEnqueue(OnPropertySync, model); //属性同步没有用到Buffer
                    break;
                default:
                    base.OnCommand4Handler(model, segment);
                    break;
            }
        }

        #region 切换主线程处理

        protected void MainThreadEnqueue(IRpcModel model)
        {
            model.Flush(); //先缓存起来, 当切换到主线程后才能得到正确的数据
            RpcAdapter.WorkerQueue.Call(OnReceiveData, model);
        }

        protected void MainThreadEnqueue(Action action)
        {
            RpcAdapter.WorkerQueue.Call(action);
        }

        protected void MainThreadEnqueue<T>(Action<T> action, T arg)
        {
            RpcAdapter.WorkerQueue.Call(action, arg);
        }

        protected void MainThreadEnqueue<T, T1>(Action<T, T1> action, T arg1, T1 arg2)
        {
            RpcAdapter.WorkerQueue.Call(action, arg1, arg2);
        }

        protected void MainThreadEnqueue(OnReceiveDataHandle action, IRpcModel model)
        {
            RpcAdapter.WorkerQueue.Call(action, model);
        }

        protected void MainThreadEnqueue(OnOperationSyncEvent action, in OperationList operList)
        {
            RpcAdapter.WorkerQueue.Call(action, operList);
        }

        #endregion

        public override UniTask<bool> Connect(string host, int port, int localPort, Action<bool> result)
        {
            Instance ??= this;
            return base.Connect(host, port, localPort, result);
        }

        /// <summary>
        /// 字段,属性同步处理线程
        /// </summary>
        protected virtual void SyncVarHandler()
        {
            var buffer = SyncVarHelper.CheckSyncVar(true, RpcAdapter.SyncVarDic);
            if (buffer != null)
                Call(NetCmd.SyncVarP2P, buffer);
        }

        protected override void OnSendFileProgressHandler(StreamProgress streamProgress) => MainThreadEnqueue(OnSendFileProgress, streamProgress);

        protected override void OnReceiveFileProgressHandler(StreamProgress streamProgress) => MainThreadEnqueue(OnReceiveFileProgress, streamProgress);

        public override void Close(bool isWait = true, int millisecondsTimeout = 100)
        {
            base.Close(isWait, millisecondsTimeout);
            if (Instance == this)
                Instance = null;
        }
    }
}